<!DOCTYPE html>
<html lang="zh-CN">
<!-- 20251019用AI制作后修改 -->
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>数据对比工具</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">

  <!-- 配置Tailwind自定义主题 -->
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: {
            primary: '#3b82f6',
            secondary: '#10b981',
            accent: '#8b5cf6',
            neutral: '#64748b',
            'neutral-light': '#f1f5f9',
            'diff-only-a': '#dbeafe',
            'diff-only-b': '#dcfce7',
            'diff-both': '#f3e8ff',
          },
          fontFamily: {
            inter: ['Inter', 'system-ui', 'sans-serif'],
          },
        },
      }
    }
  </script>

  <style type="text/tailwindcss">
    @layer utilities {
      .content-auto {
        content-visibility: auto;
      }
      .text-shadow {
        text-shadow: 0 2px 4px rgba(0,0,0,0.1);
      }
      .transition-custom {
        transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
      }
    }
  </style>
</head>

<body class="font-inter bg-gray-50 text-gray-800 min-h-screen flex flex-col">
  <!-- 顶部导航栏 -->
  <header class="bg-white shadow-md sticky top-0 z-50 transition-custom">
    <div class="container mx-auto px-4 py-4 flex justify-between items-center">
      <div class="flex items-center space-x-2">
        <i class="fa fa-code-fork text-primary text-2xl"></i>
        <h1 class="text-xl md:text-2xl font-bold text-gray-800">数据对比工具</h1>
      </div>
      <div class="flex items-center space-x-4">
        <button id="theme-toggle" class="p-2 rounded-full hover:bg-gray-100 transition-custom">
          <i class="fa fa-moon-o text-gray-600"></i>
        </button>
        <button class="p-2 rounded-full hover:bg-gray-100 transition-custom">
          <i class="fa fa-question-circle text-gray-600"></i>
        </button>
      </div>
    </div>
  </header>

  <!-- 主要内容区 -->
  <main class="flex-grow container mx-auto px-4 py-8">
    <!-- 功能说明 -->
    <div class="mb-8 bg-white rounded-xl shadow-sm p-6">
      <h2 class="text-lg font-semibold mb-2 text-gray-800">数据对比与分析</h2>
      <p class="text-gray-600">输入两组数据，系统将自动分析并展示它们的异同，同时提供详细的统计信息和多种展示格式选择。</p>
    </div>

    <!-- 数据输入区域 -->
    <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
      <!-- 第一组数据 -->
      <div class="bg-white rounded-xl shadow-sm p-6 transition-custom hover:shadow-md">
        <div class="flex items-center mb-4">
          <div class="w-3 h-3 rounded-full bg-primary mr-2"></div>
          <h3 class="font-semibold text-gray-800">第一组数据</h3>
        </div>
        <textarea id="dataSetA"
          class="w-full h-40 p-4 border border-gray-200 rounded-lg focus:ring-2 focus:ring-primary/50 focus:border-primary outline-none transition-custom resize-none"
          placeholder="请输入第一组数据，每行一个条目..."></textarea>
        <div class="mt-4 text-sm text-gray-500 flex justify-between items-center">
          <span id="countA">条目数: 0</span>
          <button class="text-primary hover:text-primary/80 transition-custom"
            onclick="document.getElementById('dataSetA').value=''; document.getElementById('countA').textContent='条目数: 0'">
            <i class="fa fa-trash-o mr-1"></i>清空
          </button>
        </div>
      </div>

      <!-- 第二组数据 -->
      <div class="bg-white rounded-xl shadow-sm p-6 transition-custom hover:shadow-md">
        <div class="flex items-center mb-4">
          <div class="w-3 h-3 rounded-full bg-secondary mr-2"></div>
          <h3 class="font-semibold text-gray-800">第二组数据</h3>
        </div>
        <textarea id="dataSetB"
          class="w-full h-40 p-4 border border-gray-200 rounded-lg focus:ring-2 focus:ring-secondary/50 focus:border-secondary outline-none transition-custom resize-none"
          placeholder="请输入第二组数据，每行一个条目..."></textarea>
        <div class="mt-4 text-sm text-gray-500 flex justify-between items-center">
          <span id="countB">条目数: 0</span>
          <button class="text-secondary hover:text-secondary/80 transition-custom"
            onclick="document.getElementById('dataSetB').value=''; document.getElementById('countB').textContent='条目数: 0'">
            <i class="fa fa-trash-o mr-1"></i>清空
          </button>
        </div>
      </div>
    </div>

    <!-- 控制选项区域 -->
    <div class="bg-white rounded-xl shadow-sm p-6 mb-8">
      <h3 class="font-semibold text-gray-800 mb-4">对比选项</h3>

      <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
        <!-- 格式控制 -->
        <div>
          <label class="block text-sm font-medium text-gray-700 mb-2">结果展示格式</label>
          <div class="flex flex-wrap gap-3">
            <label class="inline-flex items-center">
              <input type="radio" name="displayFormat" value="newline" checked class="text-primary focus:ring-primary">
              <span class="ml-2 text-sm text-gray-700">换行分隔</span>
            </label>
            <label class="inline-flex items-center">
              <input type="radio" name="displayFormat" value="space" class="text-primary focus:ring-primary">
              <span class="ml-2 text-sm text-gray-700">空格分隔</span>
            </label>
            <label class="inline-flex items-center">
              <input type="radio" name="displayFormat" value="comma" class="text-primary focus:ring-primary">
              <span class="ml-2 text-sm text-gray-700">逗号分隔</span>
            </label>
            <label class="inline-flex items-center">
              <input type="radio" name="displayFormat" value="semicolon" class="text-primary focus:ring-primary">
              <span class="ml-2 text-sm text-gray-700">分号分隔</span>
            </label>
          </div>
        </div>

        <!-- 其他选项 -->
        <div>
          <label class="block text-sm font-medium text-gray-700 mb-2">高级选项</label>
          <div class="flex flex-wrap gap-3">
            <label class="inline-flex items-center">
              <input type="checkbox" id="caseSensitive" class="text-primary focus:ring-primary">
              <span class="ml-2 text-sm text-gray-700">区分大小写</span>
            </label>
            <label class="inline-flex items-center">
              <input type="checkbox" id="trimWhitespace" checked class="text-primary focus:ring-primary">
              <span class="ml-2 text-sm text-gray-700">自动去除空格</span>
            </label>
          </div>
        </div>
      </div>

      <!-- 对比按钮 -->
      <div class="mt-6">
        <button id="compareBtn"
          class="bg-primary hover:bg-primary/90 text-white px-6 py-3 rounded-lg font-medium shadow-sm hover:shadow transition-custom flex items-center">
          <i class="fa fa-exchange mr-2"></i>开始对比
        </button>
      </div>
    </div>

    <!-- 统计信息面板 -->
    <div id="statsPanel" class="bg-white rounded-xl shadow-sm p-6 mb-8 hidden">
      <h3 class="font-semibold text-gray-800 mb-4">统计信息</h3>
      <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
        <div class="bg-neutral-light rounded-lg p-4">
          <div class="text-sm text-gray-500 mb-1">第一组数据总数</div>
          <div class="text-2xl font-bold text-primary" id="statTotalA">0</div>
        </div>
        <div class="bg-neutral-light rounded-lg p-4">
          <div class="text-sm text-gray-500 mb-1">第二组数据总数</div>
          <div class="text-2xl font-bold text-secondary" id="statTotalB">0</div>
        </div>
        <div class="bg-neutral-light rounded-lg p-4">
          <div class="text-sm text-gray-500 mb-1">两组共有的数据</div>
          <div class="text-2xl font-bold text-accent" id="statCommon">0</div>
        </div>
        <div class="bg-neutral-light rounded-lg p-4">
          <div class="text-sm text-gray-500 mb-1">数据总唯一数</div>
          <div class="text-2xl font-bold text-neutral" id="statUniqueTotal">0</div>
        </div>
        <div class="bg-diff-only-a rounded-lg p-4">
          <div class="text-sm text-gray-500 mb-1">仅第一组有的数据</div>
          <div class="text-2xl font-bold text-primary" id="statOnlyA">0</div>
        </div>
        <div class="bg-diff-only-b rounded-lg p-4">
          <div class="text-sm text-gray-500 mb-1">仅第二组有的数据</div>
          <div class="text-2xl font-bold text-secondary" id="statOnlyB">0</div>
        </div>
        <div class="bg-neutral-light rounded-lg p-4 sm:col-span-2 md:col-span-2">
          <div class="text-sm text-gray-500 mb-1">第一组与第二组的重合率</div>
          <div class="text-2xl font-bold text-accent" id="statOverlapRate">0%</div>
        </div>
      </div>
    </div>

    <!-- 结果展示区域 -->
    <div id="resultsSection" class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8 hidden">
      <!-- 仅第一组有 -->
      <div class="bg-white rounded-xl shadow-sm p-6 transition-custom hover:shadow-md">
        <div class="flex items-center justify-between mb-4">
          <div class="flex items-center">
            <div class="w-3 h-3 rounded-full bg-primary mr-2"></div>
            <h3 class="font-semibold text-gray-800">仅第一组有</h3>
          </div>
          <span class="text-sm bg-diff-only-a text-primary px-2 py-1 rounded-full" id="countOnlyA">0 项</span>
        </div>
        <div id="resultOnlyA"
          class="p-4 bg-diff-only-a/50 rounded-lg text-gray-700 text-sm break-all max-h-60 overflow-y-auto"></div>
        <div class="mt-4 flex justify-end">
          <button class="copy-btn text-primary hover:text-primary/80 text-sm transition-custom"
            data-target="resultOnlyA">
            <i class="fa fa-copy mr-1"></i>复制
          </button>
        </div>
      </div>

      <!-- 两组都有 -->
      <div class="bg-white rounded-xl shadow-sm p-6 transition-custom hover:shadow-md">
        <div class="flex items-center justify-between mb-4">
          <div class="flex items-center">
            <div class="w-3 h-3 rounded-full bg-accent mr-2"></div>
            <h3 class="font-semibold text-gray-800">两组共有</h3>
          </div>
          <span class="text-sm bg-diff-both text-accent px-2 py-1 rounded-full" id="countBoth">0 项</span>
        </div>
        <div id="resultBoth"
          class="p-4 bg-diff-both/50 rounded-lg text-gray-700 text-sm break-all max-h-60 overflow-y-auto"></div>
        <div class="mt-4 flex justify-end">
          <button class="copy-btn text-accent hover:text-accent/80 text-sm transition-custom" data-target="resultBoth">
            <i class="fa fa-copy mr-1"></i>复制
          </button>
        </div>
      </div>

      <!-- 仅第二组有 -->
      <div class="bg-white rounded-xl shadow-sm p-6 transition-custom hover:shadow-md">
        <div class="flex items-center justify-between mb-4">
          <div class="flex items-center">
            <div class="w-3 h-3 rounded-full bg-secondary mr-2"></div>
            <h3 class="font-semibold text-gray-800">仅第二组有</h3>
          </div>
          <span class="text-sm bg-diff-only-b text-secondary px-2 py-1 rounded-full" id="countOnlyB">0 项</span>
        </div>
        <div id="resultOnlyB"
          class="p-4 bg-diff-only-b/50 rounded-lg text-gray-700 text-sm break-all max-h-60 overflow-y-auto"></div>
        <div class="mt-4 flex justify-end">
          <button class="copy-btn text-secondary hover:text-secondary/80 text-sm transition-custom"
            data-target="resultOnlyB">
            <i class="fa fa-copy mr-1"></i>复制
          </button>
        </div>
      </div>
    </div>
  </main>

  <!-- 页脚 -->
  <footer class="bg-white border-t border-gray-200 py-6">
    <div class="container mx-auto px-4 text-center text-gray-500 text-sm">
      <p>数据对比工具 &copy; 2025 | 高效分析两组数据的异同</p>
    </div>
  </footer>

  <!-- 通知提示组件 -->
  <div id="notification"
    class="fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg transform translate-y-20 opacity-0 transition-all duration-300 flex items-center">
  </div>

  <script>
    // 初始化页面
    document.addEventListener('DOMContentLoaded', () => {
      // 监听文本框输入，实时更新计数
      const dataSetA = document.getElementById('dataSetA');
      const dataSetB = document.getElementById('dataSetB');
      const countA = document.getElementById('countA');
      const countB = document.getElementById('countB');

      // 计算条目数量
      const updateCount = (textarea, countElement) => {
        const text = textarea.value;
        const count = text.trim() === '' ? 0 : text.split('\n').filter(item => item.trim() !== '').length;
        countElement.textContent = `条目数: ${count}`;
      };

      dataSetA.addEventListener('input', () => updateCount(dataSetA, countA));
      dataSetB.addEventListener('input', () => updateCount(dataSetB, countB));

      // 对比按钮点击事件
      document.getElementById('compareBtn').addEventListener('click', compareData);

      // 复制按钮事件
      document.querySelectorAll('.copy-btn').forEach(btn => {
        btn.addEventListener('click', function () {
          const targetId = this.getAttribute('data-target');

          // 获取当前显示格式
          const displayFormat = document.querySelector('input[name="displayFormat"]:checked').value;

          // 根据显示格式获取分隔符
          let separator = '\n';
          if (displayFormat === 'space') separator = ' ';
          else if (displayFormat === 'comma') separator = ', ';
          else if (displayFormat === 'semicolon') separator = '; ';

          // 获取原始数据（把<br>转换为换行符）
          const originalText = document.getElementById(targetId).innerHTML.replace(/<br>/g, '\n');

          // 根据显示格式处理数据
          let formattedText = originalText;
          if (displayFormat === 'newline') {
            formattedText = originalText.replace(/\s+/g, '\n');
          } else {
            formattedText = originalText.replace(/\s+/g, separator);
          }
          // 复制到剪贴板
          navigator.clipboard.writeText(formattedText).then(() => {
            showNotification('复制成功！', 'success');
          }).catch(err => {
            showNotification('复制失败，请手动复制', 'error');
            console.error('复制失败:', err);
          });
        });
      });

      // 监听显示格式变化，重新渲染结果
      document.querySelectorAll('input[name="displayFormat"]').forEach(radio => {
        radio.addEventListener('change', () => {
          if (document.getElementById('resultsSection').classList.contains('hidden') === false) {
            compareData(); // 重新对比以应用新格式
          }
        });
      });

      // 监听高级选项变化
      document.getElementById('caseSensitive').addEventListener('change', compareData);
      document.getElementById('trimWhitespace').addEventListener('change', compareData);
    });

    // 对比数据的核心函数
    function compareData() {
      // 获取输入数据
      const textA = document.getElementById('dataSetA').value;
      const textB = document.getElementById('dataSetB').value;

      // 获取选项设置
      const caseSensitive = document.getElementById('caseSensitive').checked;
      const trimWhitespace = document.getElementById('trimWhitespace').checked;
      const displayFormat = document.querySelector('input[name="displayFormat"]:checked').value;

      // 处理数据：分割、清洗
      let setA = processData(textA, caseSensitive, trimWhitespace);
      let setB = processData(textB, caseSensitive, trimWhitespace);

      // 计算差异
      const onlyA = new Set([...setA].filter(x => !setB.has(x)));
      const onlyB = new Set([...setB].filter(x => !setA.has(x)));
      const both = new Set([...setA].filter(x => setB.has(x)));

      // 计算统计信息
      const totalA = setA.size;
      const totalB = setB.size;
      const common = both.size;
      const uniqueTotal = onlyA.size + onlyB.size + common;
      const onlyAcount = onlyA.size;
      const onlyBcount = onlyB.size;
      const overlapRate = totalA > 0 ? Math.round((common / totalA) * 100) : 0;

      // 更新统计面板
      document.getElementById('statTotalA').textContent = totalA;
      document.getElementById('statTotalB').textContent = totalB;
      document.getElementById('statCommon').textContent = common;
      document.getElementById('statUniqueTotal').textContent = uniqueTotal;
      document.getElementById('statOnlyA').textContent = onlyAcount;
      document.getElementById('statOnlyB').textContent = onlyBcount;
      document.getElementById('statOverlapRate').textContent = `${overlapRate}%`;

      // 显示统计面板
      document.getElementById('statsPanel').classList.remove('hidden');

      // 根据选择的格式显示结果
      const separator = getSeparatorByFormat(displayFormat);

      // 更新结果显示
      document.getElementById('resultOnlyA').textContent = Array.from(onlyA).join(separator);
      document.getElementById('resultBoth').textContent = Array.from(both).join(separator);
      document.getElementById('resultOnlyB').textContent = Array.from(onlyB).join(separator);

      // 确保换行分隔符正确应用
      if (displayFormat === 'newline') {
        document.getElementById('resultOnlyA').innerHTML = Array.from(onlyA).join('<br>');
        document.getElementById('resultBoth').innerHTML = Array.from(both).join('<br>');
        document.getElementById('resultOnlyB').innerHTML = Array.from(onlyB).join('<br>');
      }

      // 更新计数显示
      document.getElementById('countOnlyA').textContent = `${onlyAcount} 项`;
      document.getElementById('countBoth').textContent = `${common} 项`;
      document.getElementById('countOnlyB').textContent = `${onlyBcount} 项`;

      // 显示结果区域
      document.getElementById('resultsSection').classList.remove('hidden');

      // 平滑滚动到结果区域
      document.getElementById('statsPanel').scrollIntoView({ behavior: 'smooth', block: 'start' });
    }

    // 处理数据：分割、清洗、去重
    function processData(text, caseSensitive, trimWhitespace) {
      if (!text.trim()) return new Set();

      // 按行分割
      let items = text.split('\n');

      // 处理每个条目
      items = items.map(item => {
        // 去除首尾空格
        if (trimWhitespace) {
          item = item.trim();
        }
        // 转为小写（如果不区分大小写）
        if (!caseSensitive) {
          item = item.toLowerCase();
        }
        return item;
      }).filter(item => item !== ''); // 过滤空字符串

      // 去重并返回Set
      return new Set(items);
    }

    // 根据选择的格式获取分隔符
    function getSeparatorByFormat(format) {
      switch (format) {
        case 'newline':
          return '\n';
        case 'space':
          return ' ';
        case 'comma':
          return ', ';
        case 'semicolon':
          return '; ';
        default:
          return '\n';
      }
    }

    // 显示通知提示
    function showNotification(message, type = 'info') {
      const notification = document.getElementById('notification');

      // 设置样式
      notification.className = 'fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg transform translate-y-0 opacity-100 transition-all duration-300 flex items-center';

      // 根据类型设置不同背景色
      if (type === 'success') {
        notification.classList.add('bg-green-500', 'text-white');
        notification.innerHTML = '<i class="fa fa-check-circle mr-2"></i>' + message;
      } else if (type === 'error') {
        notification.classList.add('bg-red-500', 'text-white');
        notification.innerHTML = '<i class="fa fa-exclamation-circle mr-2"></i>' + message;
      } else {
        notification.classList.add('bg-blue-500', 'text-white');
        notification.innerHTML = '<i class="fa fa-info-circle mr-2"></i>' + message;
      }

      // 3秒后隐藏
      setTimeout(() => {
        notification.classList.remove('translate-y-0', 'opacity-100');
        notification.classList.add('translate-y-20', 'opacity-0');
      }, 3000);
    }
  </script>
</body>

</html>